﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using System;

namespace Kylin
{
    public class AssetManager
    {
        private static Dictionary<string, AssetInfo> loadedAsset = new Dictionary<string, AssetInfo>();

        // 除 Scene 以外的资源，包含group的
        public static UnityEngine.Object LoadAsset(string path)
        {
            UnityEngine.Object result = null;

#if UNITY_EDITOR
            bool useResources = true;

            useResources = PlayerPrefs.GetString("ResourceLoadTypeKey") == "Resources";
            if (useResources)
            {
                path = ResourcePath.HotFixFolderName + "/" + path;
                result = Resources.Load(path);
            }
            else
            {
#endif
                string groupName = string.Empty; string assetName = string.Empty;
                AssetHelper.ExtractGroupAndAssetName(path, out groupName, out assetName);
                if (!groupName.Equals(string.Empty))
                {
                    path = groupName;
                }

                if (loadedAsset.ContainsKey(path))
                {
                    loadedAsset[path].ReferenceCount++;
                    result = loadedAsset[path].AssetHandle;
                }
                else
                {
                    string bundlePath = AssetHelper.MakeBundleName(path);
                    AssetBundle assetBundle = AssetBundleManager.LoadAssetBundle(bundlePath);
                    if (assetBundle == null)
                    {
                        Debug.LogError("装载AssetBundle出错");
                        return null;
                    }

                    string assetKey;
                    if (assetName == string.Empty)
                    {
                        result = assetBundle.LoadAllAssets()[0];
                        assetKey = path;
                    }
                    else
                    {
                        result = assetBundle.LoadAsset(assetName);
                        assetKey = assetName;
                    }

                    if (result != null)
                    {
                        AssetInfo assetInfo = new AssetInfo();
                        assetInfo.AssetHandle = result;
                        assetInfo.ReferenceCount++;
                        loadedAsset[assetKey] = assetInfo;
                    }
                    else
                    {
                        Debug.LogError("AssetManager装载资源可能发生错误，装载路径：" + path);
                    }
                }
#if UNITY_EDITOR
            }
#endif

            return result;
        }


        private static bool BundlesInLoading = false;
        // 除 Scene 以外的资源，包含group的
        public static void LoadAssetAsync(string path, Action<UnityEngine.Object> callback)
        {
            MonoBeBehaviourManager.Instance.StartCoroutine(LoadAssetAsyncImpl(path, callback));
        }

        private static IEnumerator LoadAssetAsyncImpl(string path, Action<UnityEngine.Object> callback)
        {
            UnityEngine.Object result = null;

#if UNITY_EDITOR
            bool useResources = true;

            useResources = PlayerPrefs.GetString("ResourceLoadTypeKey") == "Resources";
            if (useResources)
            {
                path = ResourcePath.HotFixFolderName + "/" + path;
                ResourceRequest request = Resources.LoadAsync(path);
                yield return request;

                if (callback != null)
                {
                    callback(request.asset);
                }
            }
            else
            {
#endif
                string groupName = string.Empty; string assetName = string.Empty;
                AssetHelper.ExtractGroupAndAssetName(path, out groupName, out assetName);
                if (!groupName.Equals(string.Empty))
                {
                    path = groupName;
                }

                if (loadedAsset.ContainsKey(path))
                {
                    loadedAsset[path].ReferenceCount++;

                    if (callback != null)
                    {
                        callback(loadedAsset[path].AssetHandle);
                    }                    
                }
                else
                {
                    string bundlePath = AssetHelper.MakeBundleName(path);

                    while(BundlesInLoading) { yield return null;  }
                    BundlesInLoading = true;
                    yield return AssetBundleManager.LoadAssetBundleAsync(bundlePath);
                    BundlesInLoading = false;

                    AssetBundle assetBundle = AssetBundleManager.GetAssetBundleWithoutAddReference(bundlePath);
                    if (assetBundle == null)
                    {
                        Debug.LogError("加载资源的时候AssetBundle返回null，资源路径：" + path);
                        if (callback != null)
                        {
                            callback(null);
                        }
                    }
                    else
                    {
                        string assetKey;
                        if (assetName == string.Empty)
                        {
                            result = assetBundle.LoadAllAssets()[0];
                            assetKey = path;
                        }
                        else
                        {
                            result = assetBundle.LoadAsset(assetName);
                            assetKey = assetName;
                        }

                        if (result != null)
                        {
                            AssetInfo assetInfo = new AssetInfo();
                            assetInfo.AssetHandle = result;
                            assetInfo.ReferenceCount++;
                            loadedAsset[assetKey] = assetInfo;
                            if (callback != null) callback(result);
                        }
                        else
                        {
                            Debug.LogError("AssetManager装载资源可能发生错误，装载路径：" + path);
                        }
                    }
                }
#if UNITY_EDITOR
            }
#endif
        }

        // 除 Scene 以外的资源，包含group的
        public static void UnloadAsset(string path)
        {
            string groupName = string.Empty; string assetName = string.Empty; string assetKey;
            AssetHelper.ExtractGroupAndAssetName(path, out groupName, out assetName);
            if (!groupName.Equals(string.Empty))
            {
                assetKey = assetName;
                path = groupName;
            }
            else
            {
                assetKey = path;
            }

            if (!loadedAsset.ContainsKey(assetKey))
            {
                Debug.LogWarning(string.Format("字典中没有该资源存在: {0}", path));
            }
            else
            {
                loadedAsset[assetKey].ReferenceCount--;
                if (loadedAsset[assetKey].ReferenceCount == 0)
                {
                    string bundlePath = AssetHelper.MakeBundleName(path);
                    AssetBundleManager.TryUnloadAssetBundle(bundlePath);
                    Debug.Log("<color=green>尝试卸载资源： </color>" + assetKey);
                    Resources.UnloadAsset(loadedAsset[assetKey].AssetHandle);
                    loadedAsset.Remove(assetKey);
                }
            }
        }

        // 此方法用在用户使用SceneLoader装载场景之前，为做成Bundle的场景准备好所有的资源
        public static string LoadSceneAsset(string sceneName)
        {
            string result = null;

            string bundlePath = AssetHelper.MakeScenePath(sceneName).ToLower();            

            if (loadedAsset.ContainsKey(bundlePath))
            {
                Debug.LogError("该场景已经被加载过................");
            }
            else
            {
                string fullPath = ResourcePath.BundlePath + "/" + bundlePath;
                AssetBundle assetBundle = AssetBundleManager.LoadAssetBundle(bundlePath);
                if (assetBundle != null)
                {
                    string[] pathes = assetBundle.GetAllScenePaths();
                    result = Path.GetFileNameWithoutExtension(pathes[0]);

                    AssetInfo assetInfo = new AssetInfo();
                    assetInfo.AssetHandle = null;
                    assetInfo.ReferenceCount++;
                    loadedAsset.Add(bundlePath, assetInfo);
                }
                else
                {
                    Debug.LogError("Load scene assetbundle [" + fullPath + " ] failed!");
                    result = null;
                }
            }

            return result;
        }

        // 此方法目前在SceneLoader装载其他场景时使用，其他地方不应该调用（除非你已经了解透彻）
        public static void UnloadSceneAsset(string sceneName)
        {
            string bundlePath = AssetHelper.MakeScenePath(sceneName).ToLower();
            AssetBundleManager.TryUnloadAssetBundle(bundlePath);
            loadedAsset.Remove(bundlePath);
        }

        private class AssetInfo
        {
            public AssetInfo() {
                AssetHandle = null;
                ReferenceCount = 0;
            }

            public UnityEngine.Object AssetHandle {
                get; set;
            }

            public int ReferenceCount
            {
                get; set;
            }
        }


        private class AssetBundleManager
        {
            private static readonly string defaultBundleSuffix = ".nm";
            private static Dictionary<string, AssetBundleInfo> loadedBundle = new Dictionary<string, AssetBundleInfo>();

            public static AssetBundle LoadAssetBundle(string path)
            {
                AssetBundle result = null;

                bool useResources = true;
                useResources = PlayerPrefs.GetString("ResourceLoadTypeKey") == "Resources";
                if (useResources)
                {

                }
                else
                {
                    string bundleFile = AssetHelper.MakeBundleFullPath(path);

                    if (loadedBundle.ContainsKey(path))
                    {
                        result = loadedBundle[path].AssetBundleHandle;
                        loadedBundle[path].ReferenceCount++;

                        string[] dependencies = BundleManifest.GetAllDependencies(path);
                        for (int i = 0; i < dependencies.Length; ++i)
                        {
                            DependenciesReferenceAdd(dependencies[i]);
                        }
                    }
                    else
                    {
                        string[] dependencies = BundleManifest.GetAllDependencies(path);
                        for (int i = 0; i < dependencies.Length; ++i)
                        {
                            LoadAssetBundle(dependencies[i]);
                        }

                        result = AssetBundle.LoadFromFile(bundleFile);

                        AssetBundleInfo assetBundleInfo = new AssetBundleInfo();
                        assetBundleInfo.AssetBundleHandle = result;
                        assetBundleInfo.ReferenceCount++;
                        loadedBundle[path] = assetBundleInfo;
                    }
                }

                return result;
            }

            public static IEnumerator LoadAssetBundleAsync(string path)
            {
                AssetBundle result = null;

#if UNITY_EDITOR
                bool useResources = true;
                useResources = PlayerPrefs.GetString("ResourceLoadTypeKey") == "Resources";
                if (useResources)
                {

                }
                else
                {
#endif
                    string bundleFile = AssetHelper.MakeBundleFullPath(path);

                    if (loadedBundle.ContainsKey(path))
                    {
                        result = loadedBundle[path].AssetBundleHandle;
                        loadedBundle[path].ReferenceCount++;

                        string[] dependencies = BundleManifest.GetAllDependencies(path);
                        for (int i = 0; i < dependencies.Length; ++i)
                        {
                            DependenciesReferenceAdd(dependencies[i]);
                        }
                    }
                    else
                    {
                        string[] dependencies = BundleManifest.GetAllDependencies(path);
                        for (int i = 0; i < dependencies.Length; ++i)
                        {
                            yield return LoadAssetBundleAsync(dependencies[i]);
                        }

                        AssetBundleCreateRequest request = AssetBundle.LoadFromFileAsync(bundleFile);
                        yield return request;

                        AssetBundleInfo assetBundleInfo = new AssetBundleInfo();
                        assetBundleInfo.AssetBundleHandle = request.assetBundle;
                        assetBundleInfo.ReferenceCount++;
                        loadedBundle[path] = assetBundleInfo;
                    }
#if UNITY_EDITOR
                }
#endif
            }

            public static AssetBundle GetAssetBundleWithoutAddReference(string path)
            {
                if (loadedBundle.ContainsKey(path))
                {
                    return loadedBundle[path].AssetBundleHandle;
                }
                else
                {
                    Debug.LogWarning("GetAssetBundleWithoutAddReference 方法取不到 path 为" + path + "的AssetBundle。字典中没有！");
                    return null;
                }
            }

            private static void DependenciesReferenceAdd(string path)
            {
                string[] dependencies = BundleManifest.GetAllDependencies(path);
                for (int i = 0; i < dependencies.Length; ++i)
                {
                    DependenciesReferenceAdd(dependencies[i]);
                }

                loadedBundle[path].ReferenceCount++;
            }

            public static void TryUnloadAssetBundle(string path)
            {
                if (!loadedBundle.ContainsKey(path))
                {
                    Debug.LogWarning(string.Format("Path = {0}的资源没有在字典中没有找到，是不是搞错了？", path));
                }

                loadedBundle[path].ReferenceCount--;
                if (loadedBundle[path].ReferenceCount == 0)
                {
                    Debug.Log("<color=red>卸载AssetBundle： </color>" + path);
                    loadedBundle[path].AssetBundleHandle.Unload(true);
                    loadedBundle.Remove(path);
                }

                string[] dependencies = BundleManifest.GetAllDependencies(path);
                for (int i = 0; i < dependencies.Length; ++i)
                {
                    TryUnloadAssetBundle(dependencies[i]);
                }
            }

            private static AssetBundleManifest bundleManifest;

            private static AssetBundleManifest BundleManifest
            {
                get
                {
                    if (bundleManifest == null)
                    {
                        if (!File.Exists(ResourcePath.BundleFilePath))
                        {
                            Debug.LogWarning("是不是项目没有真的开始，找不到相应的资源（manifest依赖文件）。如果是真的项目这就是个错误，否则请忽略。");
                        }

                        try
                        {
                            AssetBundle ab = AssetBundle.LoadFromFile(ResourcePath.BundleFilePath);
                            bundleManifest = ab.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
                        }
                        catch (Exception e1)
                        {
                            Debug.LogException(e1);
                            Debug.Log(e1.StackTrace);
                        }

                        if (bundleManifest == null)
                        {
                            throw new Exception("Cannot get valid manifest file for asset bundle!");
                        }
                        else
                        {
                            Debug.Log(string.Format("Manifest file: {0} loaded successfully.", ResourcePath.BundlePath));
                        }
                    }

                    return bundleManifest;
                }
            }

            public class AssetBundleInfo
            {
                public AssetBundleInfo()
                {
                    AssetBundleHandle = null;
                    ReferenceCount = 0;
                }

                public AssetBundle AssetBundleHandle
                {
                    get;
                    set;
                }

                public int ReferenceCount
                {
                    get; set;
                }
            }
        }
    }
}

